﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

namespace IPTV_Playlist_Editor
{
    public partial class Editor : Form
    {
        private string fileName = "", line;
        private int channelNum = 0;
        private StreamReader playlistFile;
        private BindingList<Channel> channels = new BindingList<Channel>();

        public Editor()
        {
            InitializeComponent();
            splitContainer1.Panel2Collapsed = true;
        }

        private void openPlaylist(object sender, EventArgs e)
        {
            alertSave();
            openFile.ShowDialog();
        }

        private void importM3U()
        {
            List<string> data = new List<string>();
            while ((line = playlistFile.ReadLine()) != null)
            {
                if (line.StartsWith("#EXTINF"))
                {
                    data.AddRange(line.Substring(8).Split(','));
                    continue;
                }
                else if (line.StartsWith("#EXTTV"))
                {
                    data.AddRange(line.Substring(7).Split(';'));
                    continue;
                }
                else if (line.StartsWith("#EXTLOGO"))
                {
                    data.Add(line.Substring(9));
                    continue;
                }
                else if (line.StartsWith("udp://@rtp://"))
                {
                    data.AddRange(line.Substring(14).Split(':'));
                }
                else if (line.StartsWith("udp://"))
                {
                    data.AddRange(line.Substring(7).Split(':'));
                }

                if (data.Count == 7)
                {
                    channelNum = Convert.ToInt32(data[0]);
                    channels.Add(new Channel(channelNum, data[1].Trim(), data[2].Trim().Replace('-', ','), data[3].Trim(), data[4].Trim(), data[5].Trim(), Convert.ToInt32(data[6])));
                }
                else if (data.Count == 8)
                {
                    channelNum = Convert.ToInt32(data[0]);
                    channels.Add(new Channel(channelNum, data[1].Trim(), data[2].Trim().Replace('-', ','), data[3].Trim(), data[4].Trim(), data[6].Trim(), Convert.ToInt32(data[7]), data[5].Trim()));
                }
                
                data.Clear();
            }
            playlistFile.Close();
        }

        private void importCSV()
        {
            List<string> data = new List<string>();
            while ((line = playlistFile.ReadLine()) != null)
            {
                data.AddRange(line.Split(','));
                if (!int.TryParse(data[0].Trim(), out channelNum)) { data.Clear(); continue; }

                if (data.Count == 10)
                {
                    channels.Add(new Channel(channelNum, data[1].Trim(), data[3].Trim().Replace('-', ','), data[4].Trim(), data[6].Trim(), data[5].Split(':')[0].Trim(), Convert.ToInt32(data[5].Split(':')[1]), data[9].Trim()));
                }
                else if (data.Count == 11)
                {
                    channels.Add(new Channel(channelNum, data[1].Trim(), data[3].Trim().Replace("\"", "").Replace("-", ",") + "," + data[4].Trim().Replace("\"", "").Replace("-", ","), data[5].Trim(), data[7].Trim(), data[6].Split(':')[0].Trim(), Convert.ToInt32(data[6].Split(':')[1]), data[10].Trim()));
                }
                data.Clear();
            }
            playlistFile.Close();
        }

        private void importIPTVSCm3u()
        {
            List<string> data = new List<string>();
            int channelNumber = 1;
            while ((line = playlistFile.ReadLine()) != null)
            {
                if (line.StartsWith("#EXTINF"))
                {
                    string[] channelData = line.Substring(8).Split(',');
                    string[] channelInfo = channelData[0].Split(' ');
                    for (int i = 0; i < channelInfo.Count(); i++)
                    {
                        if (i == 0)
                        {
                            int chNumber;
                            if (int.TryParse(channelInfo[i], out chNumber) && chNumber != -1)
                                data.Add(chNumber.ToString());
                            else
                                data.Add((channelNumber++).ToString());
                            data.Add(channelData[1]);
                        }
                        else if (channelInfo[i].StartsWith("tvg-id"))
                        {
                            data.Add(channelInfo[i].Substring(7).Trim(new Char[] { ' ', '"' }));
                        }
                        else if (channelInfo[i].StartsWith("tvg-name"))
                        {
                            data.Add(channelInfo[i].Substring(9).Trim(new Char[] { ' ', '"' }));
                        }
                        else if (channelInfo[i].StartsWith("tvg-logo"))
                        {
                            data.Add(channelInfo[i].Substring(9).Trim(new Char[] { ' ', '"' }));
                        }
                        else if (channelInfo[i].StartsWith("group-title"))
                        {
                            data.Add(channelInfo[i].Substring(12).Trim(new Char[] { ' ', '"' }).Replace('-', ','));
                        }
                    }
                    continue;
                }
                else if (line.StartsWith("udp://@rtp://"))
                {
                    data.AddRange(line.Substring(14).Split(':'));
                }
                else if (line.StartsWith("udp://"))
                {
                    data.AddRange(line.Substring(7).Split(':'));
                }

                if (data.Count == 8)
                {
                    channelNum = Convert.ToInt32(data[0]);
                    channels.Add(new Channel(channelNum, data[1], data[5], null, (data[2] != null) ? data[2] : data[3], data[6], Convert.ToInt32(data[7]), data[4]));
                }
                data.Clear();
            }
            playlistFile.Close();
        }

        private void enableEditing()
        {
            saveToolStripMenuItem.Enabled = true;
            exportToolStripMenuItem.Enabled = true;
            toolStripSave.Enabled = true;
            toolStripExport.Enabled = true;
            toolStripNew.Enabled = true;
            toolStripRemove.Enabled = true;
            toolStripRefresh.Enabled = true;
        }

        private void disableEditing()
        {
            saveToolStripMenuItem.Enabled = false;
            exportToolStripMenuItem.Enabled = false;
            toolStripSave.Enabled = false;
            toolStripExport.Enabled = false;
            toolStripNew.Enabled = false;
            toolStripRemove.Enabled = false;
            toolStripRefresh.Enabled = false;
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void textBoxNumericInput(object sender, KeyPressEventArgs e)
        {
            e.Handled = !char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar);
        }

        private void toolStripNew_Click(object sender, EventArgs e)
        {
            channels.Add(new Channel(channelNum+=1, "New Channel", "", "", "", "0.0.0.0", 1000));
            channelsGrid.Rows[channels.Count - 1].Selected = true;
            channelsGrid.FirstDisplayedScrollingRowIndex = channels.Count - 1;
        }

        private void channelsGrid_SelectionChanged(object sender, EventArgs e)
        {
            if (channelsGrid.SelectedRows.Count == 0)
                return;

            int selectedRow = channelsGrid.SelectedRows[0].Index;
            if (selectedRow != 0)
                toolStripMoveUp.Enabled = true;
            else
                toolStripMoveUp.Enabled = false;
            if (channelsGrid.RowCount > 1 && selectedRow == channelsGrid.RowCount - 1)
                toolStripMoveDown.Enabled = false;
            else
                toolStripMoveDown.Enabled = true;

            toolStripDuplicate.Enabled = true;
            channelID.Text = channels[selectedRow].ID.ToString();
            errorProvider.SetError(channelID, null);
            channelName.Text = channels[selectedRow].Name;
            errorProvider.SetError(channelName, null);
            channelTags.Text = channels[selectedRow].Tag;
            channelLanguage.Text = channels[selectedRow].Language;
            channelIP.Text = channels[selectedRow].IP;
            errorProvider.SetError(channelIP, null);
            channelPort.Text = channels[selectedRow].Port.ToString();
            errorProvider.SetError(channelPort, null);
            channelEPG.Text = channels[selectedRow].EPG;
            channelImage.Text = channels[selectedRow].Image;
        }

        private void toolStripMoveUp_Click(object sender, EventArgs e)
        {
            if (channelsGrid.SelectedRows.Count == 0)
                return;

            int selectedRow = channelsGrid.SelectedRows[0].Index;
            Channel current = channels[selectedRow];
            Channel previous = channels[selectedRow - 1];
            channels.RemoveAt(selectedRow);
            int prevID = previous.ID;
            previous.ID = current.ID;
            current.ID = prevID;
            channels.Insert(selectedRow-=1, current);
            channelsGrid.Rows[selectedRow].Selected = true;
        }

        private void toolStripMoveDown_Click(object sender, EventArgs e)
        {
            if (channelsGrid.SelectedRows.Count == 0)
                return;

            int selectedRow = channelsGrid.SelectedRows[0].Index;
            Channel current = channels[selectedRow];
            Channel next = channels[selectedRow + 1];
            channels.RemoveAt(selectedRow);
            int nextID = next.ID;
            next.ID = current.ID;
            current.ID = nextID;
            channels.Insert(selectedRow+=1, current);
            channelsGrid.Rows[selectedRow].Selected = true;
        }

        private void toolStripDuplicate_Click(object sender, EventArgs e)
        {
            if (channelsGrid.SelectedRows.Count == 0)
                return;

            int selectedRow = channelsGrid.SelectedRows[0].Index;
            Channel current = channels[selectedRow++];
            channels.Insert(selectedRow, current);
            channelsGrid.Rows[selectedRow].Selected = true;
        }

        private void toolStripRemove_Click(object sender, EventArgs e)
        {
            if (channelsGrid.SelectedRows.Count == 0)
                return;

            int selectedRow = channelsGrid.SelectedRows[0].Index;

            if (channels.Count > 1 && selectedRow-1 > 0)
                channelsGrid.Rows[selectedRow - 1].Selected = true;
            else if (channels.Count > 1 && selectedRow+1 <= channels.Count-1)
                channelsGrid.Rows[selectedRow + 1].Selected = true;

            channels.RemoveAt(selectedRow);
        }

        private void toolStripRefresh_Click(object sender, EventArgs e)
        {
            List<Channel> sortByID = channels.OrderBy(i => i.ID).ToList();
            channels = new BindingList<Channel>(sortByID);
            channelsGrid.DataSource = channels;
        }

        private void savePlaylist(object sender, EventArgs e)
        {
            if (channels.Count == 0)
                return;
            saveFile.ShowDialog();
        }

        private void saveM3U()
        {
            StreamWriter file = new StreamWriter(saveFile.FileName, false, Encoding.UTF8);
            file.WriteLine("#EXTM3U");
            file.WriteLine("#EXTNAME:" + fileName);
            file.WriteLine();
            for (int i = 0; i < channels.Count; i++)
            {
                file.WriteLine("#EXTINF:" + channels[i].ID + "," + channels[i].Name);
                file.WriteLine("#EXTTV:" + channels[i].Tag.Replace(",", "-") + ";" + channels[i].Language + ";" + channels[i].EPG + ((channels[i].Image != null && channels[i].Image.Trim().Length > 0) ? ";" + channels[i].Image : ""));
                file.WriteLine("udp://@" + channels[i].IP + ":" + channels[i].Port);
                file.WriteLine();
            }
            file.Close();
        }

        private void saveCSV()
        {
            StreamWriter file = new StreamWriter(saveFile.FileName, false, Encoding.UTF8);
            file.WriteLine("Channel,Name,Locked,Group,Language,Ip,Epg 1,Epg 2,Epg 3,Logo");
            for (int i = 0; i < channels.Count; i++)
            {
                file.WriteLine(channels[i].ID + "," + channels[i].Name + ",0," + channels[i].Tag.Replace(",", "-") + "," + channels[i].Language + "," + channels[i].IP + ":" + channels[i].Port + "," + channels[i].EPG + ",,," + channels[i].Image);
            }
            file.Close();
        }

        private void saveTanoM3U()
        {
            StreamWriter file = new StreamWriter(saveFile.FileName, false, Encoding.UTF8);
            file.WriteLine("#EXTM3U");
            file.WriteLine("#EXTNAME:" + fileName);
            file.WriteLine();
            for (int i = 0; i < channels.Count; i++)
            {
                file.WriteLine("#EXTINF:" + channels[i].ID + "," + channels[i].Name);
                file.WriteLine("#EXTTV:" + channels[i].Tag + ";" + channels[i].Language + ";" + channels[i].EPG);
                if (channels[i].Image != null && channels[i].Image.Trim().Length > 0)
                    file.WriteLine("#EXTLOGO:" + channels[i].Image);
                file.WriteLine("udp://@" + channels[i].IP + ":" + channels[i].Port);
                file.WriteLine();
            }
            file.Close();
        }

        private void saveIPTVSCm3u()
        {
            StreamWriter file = new StreamWriter(saveFile.FileName, false, Encoding.UTF8);
            file.WriteLine("#EXTM3U");
            file.WriteLine("#EXTNAME:" + fileName);
            file.WriteLine();
            for (int i = 0; i < channels.Count; i++)
            {
                file.WriteLine("#EXTINF:" + channels[i].ID + " tvg-id=\"" + channels[i].EPG + "\" tvg-name=\"" + channels[i].Name.Replace(' ', '_') + "\" tvg-logo=\"" + channels[i].Image + "\" group-title=\"" + channels[i].Tag + "\"," + channels[i].Name);
                file.WriteLine("udp://@" + channels[i].IP + ":" + channels[i].Port);
                file.WriteLine();
            }
            file.Close();
        }

        private void openURL(object sender, EventArgs e)
        {
            alertSave();
            OpenURL url = new OpenURL();
            if (url.ShowDialog() != DialogResult.OK)
                return;

            fileName = Path.GetFileNameWithoutExtension(url.filePath);
            playlistFile = new StreamReader(url.filePath);
            channels.Clear();
            channelsGrid.DataSource = channels;
            switch (url.fileType)
            {
                case 1:
                    if (playlistFile.ReadLine().StartsWith("#EXTM3U"))
                        importM3U();
                    else
                        importCSV();
                    break;
                case 2:
                    importIPTVSCm3u();
                    break;
            }
            channelsCheck();
        }

        private void alertSave()
        {
            if (string.IsNullOrEmpty(fileName))
                return;

            DialogResult dialogSave = MessageBox.Show("Do you want to save your current playlist?", "Save Playlist", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if (dialogSave == DialogResult.Yes)
                saveFile.ShowDialog();
        }

        private void exportPlaylist(object sender, EventArgs e)
        {
            if (channels.Count == 0)
                return;

            Export ex = new Export(channels.ToList(), fileName);
            ex.ShowDialog();
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (About about = new About())
            {
                about.ShowDialog();
            }
        }

        private void Editor_FormClosing(object sender, FormClosingEventArgs e)
        {
            alertSave();
        }

        private void openFile_FileOk(object sender, CancelEventArgs e)
        {
            fileName = Path.GetFileNameWithoutExtension(openFile.FileName);
            playlistFile = new StreamReader(openFile.FileName);
            channels.Clear();
            channelsGrid.DataSource = channels;
            switch (openFile.FilterIndex)
            {
                case 1:
                    string fileExtension = Path.GetExtension(openFile.FileName);
                    if (fileExtension == ".m3u")
                        importM3U();
                    else if (fileExtension == ".csv")
                        importCSV();
                    break;
                case 2:
                    importIPTVSCm3u();
                    break;
            }
            channelsCheck();
        }

        private void saveFile_FileOk(object sender, CancelEventArgs e)
        {
            switch (saveFile.FilterIndex)
            {
                case 1:
                    if (Path.GetExtension(saveFile.FileName) == ".m3u")
                        saveM3U();
                    else
                        saveCSV();
                    break;
                case 2:
                    saveTanoM3U();
                    break;
                case 3:
                    saveIPTVSCm3u();
                    break;
            }
        }

        private void saveChannelInfo(object sender, CancelEventArgs e)
        {
            if (channelsGrid.SelectedRows.Count == 0)
                return;

            int selectedRow = channelsGrid.SelectedRows[0].Index;
            TextBox element = (TextBox)sender;
            string errorMsg = null;
            switch (element.Name) {
                case "channelID":
                    if (element.Text.Trim().Length == 0)
                        errorMsg = "ID field must contain non-empty integer value!";
                    else
                        channels[selectedRow].ID = Convert.ToInt32(element.Text);
                    break;
                case "channelEPG":
                    channels[selectedRow].EPG = element.Text;
                    break;
                case "channelName":
                    if (element.Text.Trim().Length == 0)
                        errorMsg = "Name field must contain non-empty string value!";
                    else
                        channels[selectedRow].Name = element.Text;
                    break;
                case "channelTags":
                    channels[selectedRow].Tag = element.Text;
                    break;
                case "channelLanguage":
                    channels[selectedRow].Language = element.Text;
                    break;
                case "channelIP":
                    if (element.Text.Trim().Length == 0)
                        errorMsg = "IP Address field must contain non-empty value!";
                    else
                        channels[selectedRow].IP = element.Text;
                    break;
                case "channelPort":
                    if (element.Text.Trim().Length == 0)
                        errorMsg = "Port field must contain non-empty integer value!";
                    else
                        channels[selectedRow].Port = Convert.ToInt32(element.Text);
                    break;
                case "channelImage":
                    channels[selectedRow].Image = element.Text;
                    break;
            }
            errorProvider.SetError(element, errorMsg);
            if (errorMsg != null)
                element.Focus();
        }

        private void validateChannelInfo(object sender, EventArgs e)
        {
            this.ValidateChildren();
        }

        private void channelsCheck()
        {
            if (channels.Count == 0)
            {
                MessageBox.Show("Selected file has incorrect structure! Please open appropriate file.", "File Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                fileName = "";
                disableEditing();
                splitContainer1.Panel2Collapsed = true;
            }
            else
            {
                splitContainer1.Panel2Collapsed = false; enableEditing();
            }
        }
    }
}
